package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.domain.dto.CategoryQuery;
import com.example.demo.domain.entity.Category;
import com.example.demo.domain.vo.CategoryVO;
import com.example.demo.exception.CustomException;
import com.example.demo.mapper.CategoryMapper;
import com.example.demo.service.CategoryService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author renliang
 */
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
    private static final Long DEFAULT_ROOT_ID = 0L;

    @Override
    public IPage<Category> customizePage(IPage<Category> page, Category param) {
        return this.page(page, getQueryWrapper(param));
    }

    @Override
    public List<Category> customizeList(CategoryQuery param) {
        if (Objects.nonNull(param.getRecursive()) && param.getRecursive()) {
            return baseMapper.customizeListWithChildren(param);
        }
        return this.baseMapper.customizeList(param);
    }

    @Override
    public Category updateCategory(Category param) {
        Category entity = this.getById(param.getId());
        if (Objects.isNull(entity)) {
            throw new CustomException("分类不存在");
        }
        param.setIsLeaf(null);
        Long parentId = param.getParentId();
        // 如果父节点发生变更需要级联更新,同时更新父节点包含子节点字段
        if (Objects.nonNull(parentId) && !entity.getParentId().equals(parentId)) {
            String oldAncestors = entity.getAncestors();
            String newAncestors = getAncestors(param.getParentId()).concat(",").concat(entity.getId().toString());
            param.setAncestors(newAncestors);
            // 查询所有的子节点
            List<Category> children = getByParentId(entity.getId(), true);
            children.forEach(a -> a.setAncestors(a.getAncestors().replaceFirst(oldAncestors, newAncestors)));
            this.updateBatchById(children);
            updateIsLeaf(parentId, false);
            if (!DEFAULT_ROOT_ID.equals(entity.getParentId())) {
                updateIsLeaf(entity.getParentId(), isLeaf(entity.getParentId()));
            }
        }
        this.updateById(param);
        return param;
    }

    @Override
    public Category addCategory(Category param) {
        Long id = IdWorker.getId();
        Long parentId = param.getParentId();
        param.setId(id);
        param.setAncestors(getAncestors(parentId).concat(",").concat(id.toString()));
        this.save(param);
        if (Objects.nonNull(parentId)) {
            updateIsLeaf(parentId, false);
        }
        return param;
    }


    @Override
    public void deleteByCategoryIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<Category> list = this.listByIds(ids);
        boolean hasChildCount = list.stream().anyMatch(a -> !a.getIsLeaf());
        if (hasChildCount) {
            throw new CustomException("当前分类存在子分类不允许删除");
        }
        Set<Long> parentIds = list.stream().map(Category::getParentId).filter(Objects::nonNull).collect(Collectors.toSet());
        this.removeBatchByIds(ids);
        for (Long parentId : parentIds) {
            updateIsLeaf(parentId, isLeaf(parentId));
        }
    }

    /**
     * 删除时级联删除
     *
     * @param ids 节点ID集合
     */
    @Override
    public void deleteWithChildByCategoryIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<Category> list = this.listByIds(ids);
        // 删除子节点
        this.baseMapper.deleteChildByParentIds(ids);
        Set<Long> parentIds = list.stream().map(Category::getParentId).filter(Objects::nonNull).collect(Collectors.toSet());
        for (Long parentId : parentIds) {
            updateIsLeaf(parentId, isLeaf(parentId));
        }
    }

    @Override
    public List<CategoryVO> tree(CategoryQuery param) {
        List<Category> list = this.customizeList(param);
        return buildTree(list);
    }

    private LambdaQueryWrapper<Category> getQueryWrapper(Category param) {
        LambdaQueryWrapper<Category> query = Wrappers.lambdaQuery();
        query.eq(Objects.nonNull(param.getId()), Category::getId, param.getId());
        query.like(StringUtils.hasText(param.getName()), Category::getName, param.getName());
        query.eq(Objects.nonNull(param.getParentId()), Category::getParentId, param.getParentId());
        query.eq(Objects.nonNull(param.getIsLeaf()), Category::getIsLeaf, param.getIsLeaf());
        query.eq(Objects.nonNull(param.getDelFlag()), Category::getDelFlag, param.getDelFlag());
        return query;
    }

    private void updateIsLeaf(Long categoryId, Boolean isLeaf) {
        this.update(Wrappers.lambdaUpdate(Category.class).set(Category::getIsLeaf, isLeaf).eq(Category::getId, categoryId));
    }

    private boolean isLeaf(Long categoryId) {
        return this.count(Wrappers.lambdaQuery(Category.class).eq(Category::getDelFlag, false).eq(Category::getParentId, categoryId)) == 0;
    }

    /**
     * 构建树形结构
     *
     * @param list 分类集
     * @return 分类树形结构
     */
    @Override
    public List<CategoryVO> buildTree(List<Category> list) {
        List<CategoryVO> result = new ArrayList<>();
        List<Long> ids = list.stream().map(Category::getId).toList();
        // 找出所有的顶级节点
        List<Category> root = list.stream().filter(a -> !ids.contains(a.getParentId())).toList();
        // 找根节点的子节点
        root.forEach(a -> {
            CategoryVO vo = new CategoryVO();
            BeanUtils.copyProperties(a, vo);
            vo.setChildren(getChildren(list, a.getId()));
            result.add(vo);
        });
        return result;
    }

    @Override
    public List<Category> getByParentId(Long parentId, Boolean cascade) {
        return this.baseMapper.getByParentId(parentId, cascade);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteCategory(List<Long> ids, Boolean cascade) {
        if (cascade) {
            this.deleteWithChildByCategoryIds(ids);
        } else {
            this.deleteByCategoryIds(ids);
        }
    }

    /**
     * 递归构建子节点
     *
     * @param list     分类列表
     * @param parentId 父节点ID
     * @return 子节点
     */
    private List<CategoryVO> getChildren(List<Category> list, Long parentId) {
        List<CategoryVO> children = new ArrayList<>();
        list.forEach(a -> {
            if (parentId.equals(a.getParentId())) {
                CategoryVO vo = new CategoryVO();
                BeanUtils.copyProperties(a, vo);
                vo.setChildren(getChildren(list, a.getId()));
                children.add(vo);
            }
        });
        return children;
    }

    /**
     * 生成父级全路径
     *
     * @param parentId 父节点
     * @return 父级全路径
     */
    private String getAncestors(Long parentId) {
        String ancestors = DEFAULT_ROOT_ID.toString();
        if (!DEFAULT_ROOT_ID.equals(parentId)) {
            Category parent = this.getById(parentId);
            if (Objects.isNull(parent)) {
                throw new CustomException("父分类不存在");
            }
            ancestors = parent.getAncestors().concat(",").concat(String.valueOf(parentId));
        }
        return ancestors;
    }
}